import { CaslAbilityService, IPolicy } from '@/policy/casl-ability.service';
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { permittedFieldsOf } from '@casl/ability/extra';
import { PERMISSION_KEY } from '../decorators/role-permission.decorators';
import { Reflector } from '@nestjs/core';
import { ConfigService } from '@nestjs/config';
import { PermissionService } from '@/permission/permission.service';
import { UserRepository } from '@/user/user.repository';
import { RoleService } from '@/role/role.service';
import { User } from '@/user/user.entity';
import { plainToInstance } from 'class-transformer';
import { SharedService } from '@/modules/shared/shared.service';
const mapSubjectToClass = (subject: string) => {
  switch (subject.toLowerCase()) {
    case 'user':
      return User;
    default:
      return subject;
  }
};
export class Article {
  title: string;
  description;
  published;
  authorId;
  private;
}
@Injectable()
export class PolicyGuard implements CanActivate {
  constructor(
    private caslAbilityService: CaslAbilityService,
    private reflector: Reflector,
    private configService: ConfigService,
    private permissionService: PermissionService,
    private userRepository: UserRepository,
    private roleService: RoleService,
    private sharedService: SharedService,
  ) {}
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const classPermission = this.reflector.get(
      PERMISSION_KEY,
      context.getClass(),
    );

    const handlePermission = this.reflector.get(
      PERMISSION_KEY,
      context.getHandler(),
    );

    const cls =
      classPermission instanceof Array
        ? classPermission.join('')
        : classPermission;
    const handler =
      handlePermission instanceof Array
        ? handlePermission.join('')
        : handlePermission;

    const right = `${cls}:${handler}`;
    const req = context.switchToHttp().getRequest();
    const { username } = req.user;

    console.log('right', right);
    const permissionPolicy: any =
      await this.permissionService.findByName(right); // 获取接口权限
    const subjects = permissionPolicy.PermissionPolicy.map(
      (policy) => policy.Policy.subject,
    );
    console.log('subjects', subjects); // [ 'User' ]
    const user = await this.userRepository.findOne(username);
    const roleIds = user.UserRole.map((role) => role.roleId);
    // 判断是否是白名单
    // 如果是whitelist中的用户对应的roleId，直接返回true
    // const whitelist = this.configService.get('ROLE_ID_WHITELIST');
    // if (whitelist) {
    //   const whitelistArr = whitelist.split(',');
    //   // 判断whitelistArr中包含roleIds中的数据，则返回true
    //   if (whitelistArr.some((o) => roleIds.includes(+o))) {
    //     return true;
    //   }
    // }
    const rolePolicy = await this.roleService.findAllByIds(roleIds);

    const rolePolicyFilterBySubjects = rolePolicy.reduce((acc: any, cur) => {
      const rolePolicy: any = cur.RolePolicy.filter((policy) => {
        return subjects.includes(policy.Policy.subject);
      });
      acc.push(...rolePolicy);
      return acc;
    }, []);

    // {
    //   type: 0,
    //   effect: 'can',
    //   action: 'read',
    //   subject: 'Article',
    //   fields: ['title', 'description'],
    //   conditions: { private: true },
    // },
    // {
    //   type: 1,
    //   effect: 'can',
    //   action: 'read',
    //   subject: 'Article',
    //   conditions: {
    //     $nor: [{ authorId: 1 }, { private: true }],
    //   },
    // },
    // {
    //   type: 2,
    //   effect: 'can',
    //   action: 'read',
    //   subject: 'Article',
    //   conditions: '({authorId}) => authorId === user.id',
    //   args: 'user',
    // },
    const policies: IPolicy[] = rolePolicyFilterBySubjects.map((o) => o.Policy);
    user.RolePolicy = rolePolicy;
    delete user.password;
    user.policies = policies;
    user.roleIds = roleIds;
    user.permissions = user.UserRole.reduce((acc, cur) => {
      return [...acc, ...cur.Role.RolePermissions];
    }, []);
    const abilities = await this.caslAbilityService.buildAbility(policies, [
      // 用户权限包括字段
      user,
      req,
      this.reflector,
    ]);
    if (policies.length === 0) {
      // 接口不需要任何数据权限控制
      return true;
    }

    let allPermissionsGranted = true;
    const tempPermissionsPolicy = [...permissionPolicy.PermissionPolicy]; // 接口权限

    for (const policy of tempPermissionsPolicy) {
      // 接口和用户权限做判断
      const { action, subject, fields } = policy.Policy;

      let permissionGranted = false;

      for (const ability of abilities) {
        // map -> {string->subject: function(user)}
        const data = await this.sharedService.getSubject(subject, user); // 获取用户信息
        const subjectTemp = mapSubjectToClass(subject);
        const subjectObj =
          typeof subjectTemp === 'string'
            ? subjectTemp
            : plainToInstance(subjectTemp, data);

        if (fields) {
          if (fields instanceof Array && fields.length > 0) {
            permissionGranted = fields.every((field) =>
              ability.can(action, subjectObj, field + ''),
            );
          } else if (fields['data']) {
            permissionGranted = fields['data'].every(
              (field) => ability.can(action, subjectObj, field + ''), // ability.rules 获取规则
            );
          }
        } else {
          permissionGranted = ability.can(action, subjectObj);
        }

        if (permissionGranted) {
          break;
        }
      }

      if (permissionGranted) {
        const index = tempPermissionsPolicy.indexOf(policy);
        if (index > -1) {
          tempPermissionsPolicy.splice(index, 1);
        }
      }
    }

    if (tempPermissionsPolicy.length !== 0) {
      allPermissionsGranted = false;
    }

    return allPermissionsGranted;
  }
}
